package com.tmall.wireless.virtualviewdemo.utils.imageloader.core;

import com.tmall.wireless.vaf.Log;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.cache.disc.DiskCache;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.cache.disc.impl.UnlimitedDiskCache;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.cache.disc.impl.ext.LruDiskCache;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.cache.disc.naming.FileNameGenerator;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.cache.disc.naming.HashCodeFileNameGenerator;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.cache.memory.MemoryCache;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.cache.memory.impl.LruMemoryCache;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.core.assist.QueueProcessingType;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.core.assist.deque.LIFOLinkedBlockingDeque;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.core.decode.BaseImageDecoder;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.core.decode.ImageDecoder;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.core.display.BitmapDisplayer;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.core.display.SimpleBitmapDisplayer;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.core.download.BaseImageDownloader;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.core.download.ImageDownloader;
import com.tmall.wireless.virtualviewdemo.utils.imageloader.utils.StorageUtils;
import ohos.app.Context;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Response;

import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class DefaultConfigurationFactory {

    //    /** Creates default implementation of task executor */
    public static Executor createExecutor(int threadPoolSize, int threadPriority,
                                          QueueProcessingType tasksProcessingType) {
        boolean lifo = tasksProcessingType == QueueProcessingType.LIFO;
        BlockingQueue<Runnable> taskQueue =
                lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>();
        return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue,
                createThreadFactory(threadPriority, "uil-pool-"));
    }


    /**
     * createTaskDistributor
     * @return Creates default implementation of task distributor
     */
    public static Executor createTaskDistributor() {
        return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-"));
    }


    /**
     * createFileNameGenerator
     * @return Creates {@linkplain HashCodeFileNameGenerator default implementation} of FileNameGenerator
     */
    public static FileNameGenerator createFileNameGenerator() {
        return new HashCodeFileNameGenerator();
    }


    /**
     * createDiskCache
     * @param context
     * @param diskCacheFileNameGenerator
     * @param diskCacheSize
     * @param diskCacheFileCount
     * @return Creates default implementation of {@link DiskCache} depends on incoming parameters
     */
    public static DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator,
                                            long diskCacheSize, int diskCacheFileCount) {
        File reserveCacheDir = createReserveDiskCacheDir(context);
        if (diskCacheSize > 0 || diskCacheFileCount > 0) {
            File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context);
            try {
                return new LruDiskCache(individualCacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize,
                        diskCacheFileCount);
            } catch (IOException e) {
                e.printStackTrace();
                // continue and create unlimited cache
            }
        }
        File cacheDir = StorageUtils.getCacheDirectory(context);
        return new UnlimitedDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
    }


    /**
     * createReserveDiskCacheDir
     * @param context
     * @return Creates reserve disk cache folder which will be used if primary disk cache folder becomes unavailable
     */
    private static File createReserveDiskCacheDir(Context context) {
        File cacheDir = StorageUtils.getCacheDirectory(context, false);
        File individualDir = new File(cacheDir, "uil-images");
        if (individualDir.exists() || individualDir.mkdir()) {
            cacheDir = individualDir;
        }
        return cacheDir;
    }


    /**
     * createMemoryCache
     * @param context
     * @param memoryCacheSize
     * @return Creates default implementation of {@link MemoryCache} - {@link LruMemoryCache}<br />
     * Default cache size = 1/8 of available app memory.
     */
    public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) {
        if (memoryCacheSize == 0) {
//            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//            int memoryClass = am.getMemoryClass();
//            if (hasHoneycomb() && isLargeHeap(context)) {
//                memoryClass = getLargeMemoryClass(am);
//            }
            int memoryClass = 64;
            memoryCacheSize = 1024 * 1024 * memoryClass / 8;
        }
        return new LruMemoryCache(memoryCacheSize);
    }

//    private static boolean hasHoneycomb() {
//        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
//    }
//
//    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
//    private static boolean isLargeHeap(Context context) {
//        return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0;
//    }

//    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
//    private static int getLargeMemoryClass(ActivityManager am) {
//        return am.getLargeMemoryClass();
//    }
//


    /**
     * createImageDownloader
     * @param context
     * @param okHttpClient
     * @return Creates default implementation of {@link ImageDownloader} - {@link BaseImageDownloader}
     */
    public static ImageDownloader createImageDownloader(Context context, OkHttpClient okHttpClient) {
        return new BaseImageDownloader(context, okHttpClient);
    }


    /**
     * createImageDecoder
     * @param loggingEnabled
     * @return Creates default implementation of {@link ImageDecoder} - {@link BaseImageDecoder}
     */
    public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
        return new BaseImageDecoder(loggingEnabled);
    }


    /**
     * createBitmapDisplayer
     * @return Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer}
     */
    public static BitmapDisplayer createBitmapDisplayer() {
        return new SimpleBitmapDisplayer();
    }


    /**
     * createThreadFactory
     * @param threadPriority
     * @param threadNamePrefix
     * @return Creates default implementation of {@linkplain ThreadFactory thread factory} for task executor
     */
    private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
        return new DefaultThreadFactory(threadPriority, threadNamePrefix);
    }

    private static class DefaultThreadFactory implements ThreadFactory {

        private static final AtomicInteger poolNumber = new AtomicInteger(1);

        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
        private final int threadPriority;

        DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
            this.threadPriority = threadPriority;
            group = Thread.currentThread().getThreadGroup();
            namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
            if (t.isDaemon()) t.setDaemon(false);
            t.setPriority(threadPriority);
            return t;
        }
    }


    private static OkHttpClient getUnsafeOkHttpClient() {
        try {
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                            Log.i("DefaultConfigurationFactory.checkClientTrusted([chain, authType]):203");
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                            Log.i("DefaultConfigurationFactory.checkServerTrusted([chain, authType]):209");
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };

            final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            builder.sslSocketFactory(sslSocketFactory);
            builder.hostnameVerifier((hostname, session) -> true);

            return builder.build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public static OkHttpClient getOkHttpClient() {
        Interceptor interceptor = chain -> {
            Response originalResponse = chain.proceed(chain.request());
            return originalResponse.newBuilder()
                    .body(originalResponse.body())
                    .build();
        };

        return getUnsafeOkHttpClient().newBuilder()
                .protocols(Collections.singletonList(Protocol.HTTP_1_1))
                .addNetworkInterceptor(interceptor)
                .build();
    }
}
